#include"index.hpp"
#include<algorithm>
#include<jsoncpp/json/json.h>
#include"log.hpp"
namespace ns_searcher
{

  struct InvertedElemPrint
  {
    uint64_t doc_id;
    int weight;
    std::vector<std::string> words;
    InvertedElemPrint():doc_id(0),weight(0)
    {}
  };

  class Searcher
  {
    private:
      ns_index::Index *index;//供系统进行查找的索引
    public:
      Searcher(){};
      ~Searcher(){};
    public:
      void InitSearcher(const std::string& input)
      {
        //1.获取或者创建index对象
        // index = ns_index::Index::GetInstance();
        index=ns_index::Index::GetInstance();
       // std::cout<<"创建单例成功"<<std::endl;
       LOG(NORMAL,"创建单例成功"); 
        //2.根据index的对象建立索引 
         index->BuildIndex(input);
       LOG(NORMAL,"创建正排和倒排索引成功"); 
        // std::cout<<"创建正排和倒排索引成功"<<std::endl;
      }
      //query:搜索关键字
      //son_string:返回给用户浏览器的搜索结果
      void Search(const std::string& query,std::string* json_string)
      {
        //1.分词:将query进行分词i
        std::vector<std::string> words;
        ns_util::JiebaUtil::CutString(query,&words);
        //2.触发:根据分好的每一个词进行index查找
        std::vector<InvertedElemPrint> inverted_list_all;//内部结构是invertedelemPrint
        std::unordered_map<uint64_t,InvertedElemPrint> token_map;
        for(auto word:words)
        {
           boost::to_lower(word);
           ns_index::InvertedList *inverted_list=index->GetInvertedList(word);//Get这个函数是通过word找到倒排拉链
           if(inverted_list==nullptr)
             continue;
           //有一个不完美的地方：不同的关键字可能对应相同的文档
      //   inverted_list_all.insert(inverted_list_all.end(),inverted_list->begin(),inverted_list->end());
          //去重
           for(const auto&elem:*inverted_list)
          {
            auto& item=token_map[elem.doc_id];//[]:如果存在直接获取，如果不存在新建
            item.doc_id=elem.doc_id;//如果此时是新建的，就需要把i付过去
            item.weight+=elem.weight;
            item.words.push_back(elem.word);
          }
        }
        for(auto& item: token_map)
        {
          inverted_list_all.push_back(std::move(item.second));
        }

        //3.合并排序:汇总查找结果，按照相关性（weight）进行降序排序
        //std::sort(inverted_list_all.begin(),inverted_list_all.end(),[](const ns_index::InvertedElem& e1,const ns_index::InvertedElem& e2){
        //  return e1.weight>e2.weight;});//第三个参数高亮一个lambda表达式
       
        std::sort(inverted_list_all.begin(),inverted_list_all.end(),[](const InvertedElemPrint& e1,const InvertedElemPrint& e2){
          return e1.weight>e2.weight;});//第三个参数高亮一个lambda表达式
        //4.构建:根据查找出来的结果，构建json串
      Json::Value root;
      for(auto item:inverted_list_all)
      {
        ns_index::DocInfo* doc=index->GetForwardIndex(item.doc_id);
        if(doc==nullptr)
          continue;
        Json::Value elem;
        elem["title"]=doc->doc_title;
        elem["desc"]=GetDesc(doc->doc_content,item.words[0]);//content是去标签的，但是我们只想要一部分 TODO
        elem["url"]=doc->doc_url;
        elem["id"]=(int)doc->doc_id;
        elem["weight"]=item.weight;

        root.append(elem);
      }
       Json::StyledWriter writer;
       *json_string=writer.write(root);
      }
      std::string GetDesc(const std::string& html_content,const std::string& word)
      {
           //找到word在html_content中的首次出现的位置，往前找50字节，往后找到100字节
           const std::size_t prev_step=50;
           const std::size_t next_step=100;
           //1.找到word在html_content中首次出现的位置
      //     std::size_t pos=html_content.find(word);
       //    if(pos==std::string::npos)
       //    {
       //      return "None1";
       //    }
       // 上述代码存在问题，因为word在map插入的时候进行了小写转换，而html_content中是存在大写的
        
           auto iter=std::search(html_content.begin(),html_content.end(),word.begin(),word.end(),[](int x,int y){ return(std::tolower(x)==tolower(y));});
           std::size_t pos=std::distance(html_content.begin(),iter);//该函数返回的是元素个数

           //2.获取start，end 
           std::size_t start=0;
           std::size_t end=html_content.size()-1;
           //3.更新位置
           if(pos>start+prev_step) start=pos-prev_step;
           if(pos+next_step<end) end=pos+next_step;
           //3.截取子串
           if(start>=end)return "None2";
           return html_content.substr(start,end-start);
           
      }
  };
}
